#ifndef __M__MATCHER_H__
#define __M__MATCHER_H__
#include<list>
#include <mutex>
#include <condition_variable>
#include "room.hpp"

template<class T>
class match_queue
{
private:
    /*由于可能要删除中间数据，因此使用双向链表，而不使用队列*/
    std::list<T> _list;
    //保证线程安全
    std::mutex _mutex;
    //使用条件变量实现阻塞，在队列中元素个数小于2的时候进行阻塞
    std::condition_variable _cond;
public:
    /*入队*/
    void push(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        /*每次有玩家进入匹配队列后，唤醒线程*/
        _cond.notify_all();
    }

    /*出队*/
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()==true)
        {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }

    /*移除指定元素*/
    void remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
    /*获取队列元素个数*/
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }

    /*判断队列是否为空*/
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }

    /*阻塞等待*/
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
};

class matcher
{
private:
    /*普通队列*/
    match_queue<uint64_t> _q_normal;
    /*高手队列*/
    match_queue<uint64_t> _q_hight;
    /*大神队列*/
    match_queue<uint64_t> _q_super;
    /*普通线程*/
    std::thread _th_normal;
    /*高手线程*/
    std::thread _th_hight;
    /*大神线程*/
    std::thread _th_super;
    room_manager *_rm;
    user_table *_ut;
    online_manager *_om;
private:
    void handle_match(match_queue<uint64_t>& mq)
    {
        while(1)
        {
            /*判断队列中玩家个数是否大于2*/
            while(mq.size()<2)
            {
                mq.wait();
            }
            /*大于2，将两个玩家出队*/
            uint64_t uid1,uid2;
            bool ret = mq.pop(uid1);
            if(ret==false)
            {
                continue;
            }
            ret = mq.pop(uid2);
            if(ret==false)
            {
                continue;
            }
            /*两个玩家出队后，获取对应的通信连接,然后判断是否依然连接在线*/
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(conn1.get()==nullptr)
            {
                this->add(uid1);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn2.get()==nullptr)
            {
                this->add(uid2);
                continue;
            }
            /*获取连接后，为他们创建房间并且添加进房间*/
            room_ptr rp = _rm->create_room(uid1,uid2);
            if(rp.get()==nullptr)
            {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            /*将信息返回*/
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void _th_normal_entry(){return handle_match(_q_normal);}
    void _th_hight_entry(){return handle_match(_q_hight);}
    void _th_super_entry(){return handle_match(_q_super);}
public:
    matcher(room_manager *rm,user_table* ut,online_manager* om)
        :_rm(rm),_ut(ut),_om(om)
        ,_th_normal(std::thread(&matcher::_th_normal_entry,this))
        ,_th_hight(std::thread(&matcher::_th_hight_entry,this))
        ,_th_super(std::thread(&matcher::_th_super_entry,this))
    {
        DLOG("游戏匹配模块初始化完毕...");
    }

    bool add(uint64_t uid)
    {
        /*根据uid，获取到玩家的信息*/
        Json::Value user;
        bool ret = _ut->select_by_id(uid,user);
        if(ret==false)
        {
            DLOG("获取玩家:%d 信息失败",uid);
            return false;
        }
        int score = user["score"].asInt();
        if(score < 2000)
        {
            _q_normal.push(uid);
        }
        else if(score>=2000 && score < 3000)
        {
            _q_normal.push(uid);

        }
        else
        {
            _q_normal.push(uid);
        }
        return true;
    }

    bool del(uint64_t uid)
    {
        Json::Value user;
        bool ret = _ut->select_by_id(uid,user);
        if(ret==false)
        {
            DLOG("获取玩家:%d 信息失败",uid);
            return false;
        }
        int score = user["score"].asInt();
        if(score<2000)
        {
            _q_normal.remove(uid);
        }
        else if(score>=2000 && score<3000)
        {
            _q_hight.remove(uid);
        }
        else
        {
            _q_super.remove(uid);
        }
        return true;
    }

};

#endif